﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Text;
using RentalOfCars.Domain.Entities;
using RentalOfCars.Infrastructure.ClientServerInterface;
using RentalOfCars.Server.Domain.ClientServerInterface;
using RentalOfCars.Server.Domain.Database;
using System.Security.Cryptography;

namespace RentalOfCars.Server.Infrastructure.ClientServerInterface
{
    public class DbAccessSession : IDbAccessSession
    {
        private readonly IDbAccessor _accessor;
        private static IDictionary<string, IEmployee> _sessions = new Dictionary<string, IEmployee>();

        /// <summary>
        /// Main constructor.
        /// </summary>
        /// <param name="accessor">Data access layer. Must be opened.</param>
        public DbAccessSession(IDbAccessor accessor)
        {
            _accessor = accessor;
        }

        #region IDbAccessSession Members

        public IEmployee Login(string login, string password)
        {
            IEmployee employee = _accessor.GetEmployee(login);

            if (employee == null)
            {
                return null;
            }

            var pass = employee.Name.Substring(0, 1) + employee.LastName.Substring(0, 1) + password;
            var md5 = new MD5CryptoServiceProvider();
            var originalBytes = Encoding.UTF8.GetBytes(pass);
            var encodedBytes = md5.ComputeHash(originalBytes);
            var codedPass = BitConverter.ToString(encodedBytes);

            if (employee.Password != codedPass)
            {
                throw new InvalidUserCredentialsException("Ivalid user name or password.");
            }

            string id = OperationContext.Current.SessionId;
            if (!_sessions.ContainsKey(id))
            {
                _sessions.Add(new KeyValuePair<string, IEmployee>(id, employee));
            }

            return employee;
        }

        public IList<IClient> GetClients()
        {
            CheckSession();

            return _accessor.GetClients();
        }

        public IList<IClient> GetRegularClients()
        {
            CheckSession();

            return _accessor.GetRegularClients();
        }

        public IList<IClient> GetBestClients()
        {
            CheckSession();

            return _accessor.GetBestClients();
        }

        public IList<IClient> GetFineClients()
        {
            CheckSession();

            return _accessor.GetFineClients();
        }

        public IClient AddClient(IClient client)
        {
            string id = OperationContext.Current.SessionId;
            IEmployee employee;
            if (!_sessions.TryGetValue(id, out employee))
            {
                throw new UnauthorizedAccessException("There is no session for this call.");
            }

            if (employee.Class.Description != "Manager")
            {
                throw new UnauthorizedAccessException("There are no rights to call this method.");
            }

            return _accessor.AddClient(client);
        }

        public void DeleteClient(int clientId)
        {
            string id = OperationContext.Current.SessionId;
            IEmployee employee;
            if (!_sessions.TryGetValue(id, out employee))
            {
                throw new UnauthorizedAccessException("There is no session for this call.");
            }

            if (employee.Class.Description != "Manager")
            {
                throw new UnauthorizedAccessException("There are no rights to call this method.");
            }

            _accessor.DeleteClient(clientId);
        }

        public IList<IDiscount> GetDiscounts()
        {
            CheckSession();

            return _accessor.GetDiscounts();
        }

        public void Logoff()
        {
            string id = OperationContext.Current.SessionId;

            if (_sessions.ContainsKey(id))
            {
                _sessions.Remove(id);
            }
        }

        public IList<IEmployeeClass> GetEmployeeClasses()
        {
            CheckSession();

            return _accessor.GetEmployeeClasses();
        }

        public IEmployee AddEmployee(IEmployee employee)
        {
            string id = OperationContext.Current.SessionId;
            IEmployee empl;
            if (!_sessions.TryGetValue(id, out empl))
            {
                throw new UnauthorizedAccessException("There is no session for this call.");
            }

            if (empl.Class.Description != "Manager")
            {
                throw new UnauthorizedAccessException("There are no rights to call this method.");
            }

            return _accessor.AddEmployee(employee);
        }

        public IList<IEmployee> GetEmployees()
        {
            CheckSession();

            return _accessor.GetEmployees();
        }

        public void FireEmployee(IEmployee employee)
        {
            string id = OperationContext.Current.SessionId;
            IEmployee empl;
            if (!_sessions.TryGetValue(id, out empl))
            {
                throw new UnauthorizedAccessException("There is no session for this call.");
            }

            if (empl.Class.Description != "Manager")
            {
                throw new UnauthorizedAccessException("There are no rights to call this method.");
            }

            _accessor.FireEmployee(employee);
        }

        private void CheckSession()
        {
            string id = OperationContext.Current.SessionId;
            if (!_sessions.ContainsKey(id))
            {
                throw new UnauthorizedAccessException("There is no session for this call.");
            }
        }

        public IList<ICar> GetCars()
        {
            CheckSession();

            return _accessor.GetCars();
        }

        public ICar AddCar(ICar car)
        {
            string id = OperationContext.Current.SessionId;
            IEmployee employee;
            if (!_sessions.TryGetValue(id, out employee))
            {
                throw new UnauthorizedAccessException("There is no session for this call.");
            }

            if (employee.Class.Description != "Manager")
            {
                throw new UnauthorizedAccessException("There are no rights to call this method.");
            }

            return _accessor.AddCar(car);
        }

        public void DeleteCar(int carId)
        {
            string id = OperationContext.Current.SessionId;
            IEmployee employee;
            if (!_sessions.TryGetValue(id, out employee))
            {
                throw new UnauthorizedAccessException("There is no session for this call.");
            }

            if (employee.Class.Description != "Manager")
            {
                throw new UnauthorizedAccessException("There are no rights to call this method.");
            }

            _accessor.DeleteClient(carId);
        }

        public IList<IBrand> GetBrands()
        {
            CheckSession();

            return _accessor.GetBrands();
        }

        public IList<IFeature> GetFeatures()
        {
            CheckSession();

            return _accessor.GetFeatures();
        }

        #endregion
    }

}